package operator;

import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;
import javax.swing.JScrollBar;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import components.MPop;
import platform.MeanLabel;
import common.Constant;
import common.Singleton;
import view.Main;
import view.ViewControl;
import vo.Mean;

/**
 * 意象事务处理员。负责所有意象相关操作
 *
 * @author 柴家琪 添加意象流程：创建Mean对象-->加入reserves预备集合-->添加
 */
public class Handler {

    private Main main;
    private OUtil outil;
    private ViewControl view;
    private ContextHandler contextHandler;

    private JTextPane textPane;
    private StyledDocument doc;
    private SimpleAttributeSet attr = new SimpleAttributeSet();
    private String source, context, preText;
    private TreeMap<Integer, Integer> pages = new TreeMap<>();
    /**
     * 用来记录当前文章中显示的字段<br/>
     * 每次恢复全黑后就清空，添加新意象时会将此集合中的全部添加<br/>
     * 点击意象也会添加新对象进入此集合，但只用于显示
     */
    private ArrayList<Mean> reserves = new ArrayList<>(); // 预备
    private ArrayList<Integer> hidden = new ArrayList<>(); // 隐含
    public ArrayList<Mean> mainMean = new ArrayList<>(); // 所有主意象（要保证两个集合中的主意象同源）
    public ArrayList<Mean> allMean = new ArrayList<>();
    private int l_of_press, l_of_release;

    // 左边鼠标再次点击是否恢复
    private boolean recovery = true;
    // 编辑模式，位置添加模式
    public boolean editModel = false, posAdding = false;

    private final Color Recover = Color.black, SelfSelect = Color.green;

    private boolean opened = false, saved = true;
    public int Offset = 0;// 偏移量

    private static Handler handler;
    private Handler() {}
    public static Handler getHandler() {
        if (handler == null) {
            handler = new Handler();
            handler.main = Main.main;
            handler.outil = Singleton.oUtil();
            handler.view = Singleton.vc();
            handler.contextHandler = Singleton.contextHandler();
        }
        return handler;
    }

    /**
     * 开始主动处理一些事务
     */
    public void SetUp() {
        StyleConstants.setFontFamily(attr, Constant.boldfaceName);
//		int x=Main.presentPage;
        textPane = main.getTextPane();
        textPane.setSelectedTextColor(new Color(240, 245, 255));
        textPane.setSelectionColor(new Color(0, 162, 255));
        textPane.addMouseListener(new MouseAdapter() {
            String selected = null;

            public void mouseReleased(MouseEvent e) {
                l_of_release = textPane.getCaretPosition();
                if (e.getButton() == 1 && !editModel) {
                    selected = textPane.getSelectedText();
                    if (l_of_press < l_of_release) {
                        if (posAdding) {
                            modifyPointedText(new Mean(selected, l_of_press), Recover);
                            if (showPhrase(view.preSelected.getText(), l_of_press, l_of_release, SelfSelect) != null)
                                view.showMenus_Confirm_Revoke();
                            else
                                view.showMenus_Revoke();
                        } else {
                            if (recovery)
                                showPop(selected, e.getX(), e.getY());
                            else if (selected != null)
                                modifyPointedText(new Mean(selected, l_of_press), SelfSelect);
                        }
                    } else {
                        if (posAdding) {
                            if (l_of_release != l_of_press) {
                                modifyPointedText(new Mean(selected, l_of_release), Recover);
                                if (showPhrase(view.preSelected.getText(), l_of_release, l_of_press,
                                        SelfSelect) != null)
                                    view.showMenus_Confirm_Revoke();
                                else
                                    view.showMenus_Revoke();
                            } else
                                view.showMenus_Revoke();
                        } else {
                            if (recovery)
                                showPop(selected, e.getX(), e.getY());
                            else if (selected != null)
                                modifyPointedText(new Mean(selected, l_of_release), SelfSelect);
                        }
                    }
                } else {
                    view.callBackAll();
                }
            }

            public void mousePressed(MouseEvent e) {
                if (!editModel) {
                    if (recovery) {// 非编辑模式下
                        recoveryTextColor();
                    }
                }
                l_of_press = textPane.getCaretPosition();// error point:在复原后再获取当前点
            }
        });
        doc = textPane.getStyledDocument();
        view.SetUp();
    }

    public int getLeftPositionOfSelectedText() {
        return l_of_press > l_of_release ? l_of_release : l_of_press;
    }

    public int getRightPositionOfSelectedText() {
        return l_of_press > l_of_release ? l_of_press : l_of_release;
    }

    /**
     * 向文本域中添加新的内容
     */
    public void addNew() {
        clear(false);
        textPane.setText(""); // 清空文本域
        if (context == null) { // context为空说明是新doc文件
            context = source;
            setPgs(); // 先设置分段，添加空格后重新分段
            preText = context = addBlank(source);
        }
        setPgs();
        modifyColor(Recover);
        reInsertString(Recover, context);
        opened = true;
        saved = true;

        main.getMenuPage().setEnableOfButtonsOnMainPage();
        view.info_refreshInfo(view.ALL);
        view.callBackAll();
        // 设置滚动条位置为0
        JScrollBar bar = main.leftScroller.getVerticalScrollBar();
        if (bar != null) {
            bar.setValue(0);
        }
        textPane.setCaretPosition(0);
    }

    public void setPgs() {
        int index = 1;
        pages.clear();
        pages.put(0, 0);
        int i;
        char chars[] = context.toCharArray();
        for (i = 0; i < chars.length; i++) {
            if (chars[i] == 10 || chars[i] == '\0' || chars[i] == 13) {
                chars[i] = 10;// error point:doc中的换行是13，文本域对于字符13是不会自动换行的，要换成10
                i++;
                if (i == context.length()) {
                    pages.put(index++, i);
                    break;
                }
                boolean stop = false;
                while (chars[i] == 10 || chars[i] == '\0' || chars[i] == 13) {
                    chars[i] = 10;
                    i++;
                    if (i == context.length()) {
                        pages.put(index++, i);
                        stop = true;
                        break;
                    }
                }
                if (stop)
                    break;
                pages.put(index++, i);
            }
        }
        context = new String(chars);
    }

    private String addBlank(String source) {
        StringBuilder context = new StringBuilder();
        for (Integer index : pages.keySet()) {
            int top = pages.get(index);
            if (top + 1 < source.length()) {
                if (!source.substring(top, top + 2).equals("　　"))
                    context.append("　　");
                if (pages.containsKey(index + 1))
                    context.append(source.substring(top, pages.get(index + 1)));
                else
                    context.append(source.substring(top));
            }
        }
        return context.toString();
    }

    public void setPage(int page) {
        recoveryTextColor();// error point:转换页面时要把位置集合清空，否则在新页面会有误判
        if (page == 0)
            reInsertString(Color.black, preText = context);
        else
            reInsertString(Color.BLACK,
                    preText = context.substring(page == 1 ? 0 : pages.get(page - 1), pages.get(page)));
        textPane.setCaretPosition(0);// 设置光标位置为0
        view.attr_addAll();
    }

    private void modifyColor(Color modifyTo) {
        StyleConstants.setForeground(attr, modifyTo);
    }

    /**
     * 改变指定位置的文字颜色
     *
     * @param from 开始位置
     * @param len  修改长度
     */
    private void modify(int from, int len) {
        try {
            String copy = doc.getText(from, len);
            doc.remove(from, len); // 移除部分文字
            doc.insertString(from, copy, attr); // 插入新格式的文字
        } catch (BadLocationException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 移除文字，一般是隐含位置的删除
     */
    private void eraseText(int from, int len) {
        try {
            doc.remove(from, len); // 移除部分文字
        } catch (BadLocationException ex) {
            System.out.println("BadLocationException from:" + from + " len:" + len);
        }
    }

    /**
     * 增加文字，一般是显示隐含位置时
     */
    private void insert(int from, String str) {
        try {
            doc.insertString(from, str, attr); // 插入新格式的文字
        } catch (BadLocationException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    public Mean getLocationInAll(String text, int a, int b) {
        Mean temp = null;
        int from, i, j;
        char c[] = context.toCharArray();
        char s[] = text.toCharArray();
        for (i = a; i < b; i++) {
            if (c[i] == s[0]) {
                from = i;
                for (j = 1; j < s.length; j++) {
                    i++;
                    if (i < context.length()) {
                        if (c[i] != s[j])
                            break;
                    } else
                        break;
                }

                if (j == s.length) {
                    if (temp == null)
                        temp = new Mean(text, from);
                    else
                        temp.addLocation(from);
                }
            }
        }
        return temp;
    }

    private Mean showPhrase(String text, int a, int b, Color aim) {
        Mean temp = getLocationInAll(text, a, b);
        if (temp != null)
            modifyPointedText(temp, aim);
        return temp;
    }

    /**
     * 显示意象的所有位置
     */
    public void showMean(Mean mean, Color aim) {
        modifyPointedText(mean, aim);
    }

    /**
     * 手动选择
     */
    public void selfSelect() {
        recovery = false;
        if (l_of_press < l_of_release)
            modifyPointedText(new Mean(textPane.getSelectedText(), l_of_press), SelfSelect);
        else
            modifyPointedText(new Mean(textPane.getSelectedText(), l_of_release), SelfSelect);
    }

    /**
     * 显示隐藏的意象
     */
    private void showHidden(Mean mean) {
        if (mean.getText().equals(Constant.HIDDEN)) {
            if (main.presentPage == mean.getSuitPage()) {
                showHidden(mean.getPageFrom(), false);
            } else {
                showHidden(mean.getLocation(), false);
//				for (Integer i : mean.getLocations()) {
//					showHidden(i, false);
//				}
            }
        }
    }

    /**
     * @param position 位置
     * @param temp     临时显示
     */
    private void showHidden(int position, boolean temp) {
        int real = position < 0 ? -position : position;
        if (real < preText.length()) {
            modifyColor(Color.magenta);
            insert(real + Offset, Constant.HIDDEN);
            textPane.setCaretPosition(real);
            if (temp)
                hidden.add(position);
        }
    }

    private void showHiddenTemp() {
        showHidden(l_of_release, true);
    }

    /**
     * 插入指定文本
     */
    private void reInsertString(Color color, String str) {
        textPane.setText("");
        modifyColor(color);
        try {
            doc.insertString(0, str, attr);
        } catch (BadLocationException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 恢复全黑
     */
    public void recoveryTextColor() {
        recovery = true;
        if (!reserves.isEmpty()) {
            for (Mean reserve : reserves) {
                modifyPointedText(reserve, Recover);
            }
            reserves.clear();
        }
        if (posAdding) {
            view.callBackAll();
            view.showMenus_Revoke();
        }
        if (!hidden.isEmpty()) {
            recoveryHiddenMean(hidden, -1);
            hidden.clear();
        }
    }

    /**
     * 恢复隐含位置
     */
    private void recoveryHiddenMean(Collection<Integer> c, int pageFrom) {
        modifyColor(Recover);
        Iterator<Integer> ii = c.iterator();
        if (pageFrom == -1)
            while (ii.hasNext()) {
                eraseText(ii.next(), 1);
            } else {
            eraseText(pageFrom, 1);
        }
    }

    private void recoveryHiddenMean(int location, int pageFrom) {
        if (pageFrom == -1) {
            eraseText(location, 1);
        } else {
            eraseText(pageFrom, 1);
        }
    }

    public void directAdd(boolean main) {
        recovery = true;
        if (l_of_press < l_of_release)
            addToReserves(new Mean(textPane.getSelectedText(), l_of_press), true);
        else if (l_of_press > l_of_release)
            addToReserves(new Mean(textPane.getSelectedText(), l_of_release), true);
        else {
            if (hidden.contains(l_of_release))
                hidden.remove(hidden.indexOf(l_of_release));// error point:隐含意象添加时要把hidden数组里删除，否则会两次删除造成错误
            addToReserves(new Mean(Constant.HIDDEN, l_of_press), true);
        }
        if (main)
            addNewMainMean();
        else
            view.showSelector(true);
    }

    private void addToReserves(Mean sel, boolean temp) {
        boolean notfound = true;
        for (Mean me : reserves) {
            if (temp && me.getText().equals(sel.getText())) {// error point:对于隐含意象，文字相同，而被认为是同一意象
                notfound = false;
                me.setLocation(sel.getLocation());
                break;
            }
        }
        if (notfound)
            reserves.add(sel);
    }

    /**
     * 修改指定位置的字符串到指定颜色
     */
    private void modifyPointedText(Mean p_m, Color aim) {
//		Mean p_m=mean.clone();
        if (p_m.getText() != null) {
            if (aim != Recover) { // 对于选择新的意象时，相同文字的归为同一意象（附属于一主意象下的副意象）
                addToReserves(p_m, false); // 添加至预备队
                if (p_m.getText().equals(Constant.HIDDEN)) {
                    showHidden(p_m);
                    return;
                }
            } else if (p_m.getText().equals(Constant.HIDDEN)) {
                recoveryHiddenMean(p_m.getLocation(), main.presentPage == 0 ? -1 : p_m.getPageFrom());
                return;
            }
            // 未返回，说明是非隐含意象。》》非隐含意象的显示和恢复代码相同。
            modifyColor(aim);
            if (main.presentPage == 0) {
                modify(p_m.getLocation(), p_m.getLength());
            } else if (main.presentPage == p_m.getSuitPage()) {
                modify(p_m.getPageFrom() + Offset, p_m.getLength());
            }
        }
    }

    private void showPop(String selected, int x, int y) {
        view.callBackAll();
        view.reSetOrder();
        MPop mp;
        if (selected != null) {

            mp = new MPop(MPop.Real);
        } else {
            showHiddenTemp();
            mp = new MPop(MPop.Hidden);
        }
        mp.show(textPane, x, y);
    }

    // 命令部分***********
    public void addNewMainMean() {
        Iterator<Mean> i = reserves.iterator();
        boolean first = true;
        Mean firstMean = null;
        while (i.hasNext()) {
            Mean mean = i.next();
            if (first) {
                Mean.turnToMain(mean);
                first = false;
                firstMean = mean;
            } else {
                firstMean.addNext(mean);
                mean.setDescription();
            }
        }
        if (firstMean != null) {
            firstMean.setDescription();
            view.attr_addAll();
            modified();
        } else
            System.err.println("FIRST MEAN NULL (Handler addNewMainMean)");
    }


    public int getGroupId() {
        int i = (int) (Math.random() * 1000000);
        while (existId(i)) {
            i = (int) (Math.random() * 1000000);
        }
        return i;
    }

    private boolean existId(int id) {
        for (Mean aMainMean : mainMean) {
            if (aMainMean.getGroup_id() == id)
                return true;
        }
        return false;
    }

    public boolean selectedIsExisted() {
        return allMean.contains(reserves.get(0));
    }


    public void delete() {
        view.callBackAll();
        switch (view.preSelected.type) {
            case MeanLabel.MainMeanTop:
            case MeanLabel.ViceMeanTop:
                deleteMean(view.preSelected.sourceMean);
                view.deleteMean(view.preSelected);
                break;
        }
        if (view.preSelected.type != MeanLabel.Location)
            // 当是位置面板时，要打开位置选择面板，暂不恢复
            view.reSetOrder();
    }

    public void deleteMean(Mean m) {
        if (m.getInner_id() == 0) {
            ArrayList<Mean> ready = new ArrayList<>();
            for (Mean next : allMean) {
                if (next.getGroup_id() == m.getGroup_id())
                    ready.add(next);
            }
            allMean.removeAll(ready);
            mainMean.remove(m);
        } else {
            allMean.remove(m);
            if (m.getNext() != null)
                m.getLast().setNext(m.getNext());
            else
                m.getLast().setNext(null);
        }
        modified();
    }

    public void cancled() {
        view.callBackAll();
    }

    // 合并
    public void combineTo(Mean to) {
        if (reserves.size() != 0 || hidden.size() != 0) {
            for (Mean mean : reserves) {
                to.addNext(mean);
                mean.setDescription();
            }
            for (Integer aHidden : hidden) {
                Mean mean = new Mean(Constant.HIDDEN, aHidden);
                to.addNext(mean);
                mean.setDescription();
            }

            to.setDescription();
            view.attr_addAll();
            recoveryTextColor();
            modified();
            return;
        }
        if (view.preSelected != null) {
            if (view.preSelected.sourceMean.getGroup_id() == to.getGroup_id())
                return;
            Mean selected = view.preSelected.sourceMean;
            if (selected.getInner_id() == 0)
                mainMean.remove(selected);
            selected.turnMainToViceOf(to);
            view.attr_addAll();
            modified();
        }
    }

    public void apart() {
        Mean selected;
        if (view.preSelected != null) {
            selected = view.preSelected.sourceMean;
            if (selected.getInner_id() == 0) {
                apart2(selected.getNext());
                selected.setNext(null);
            } else {
                selected.getLast().setNext(selected.getNext());
                Mean.turnToMain(selected);
            }
        }
        view.callBackAll();
        modified();
    }

    private void apart2(Mean mean) {
        if (mean.haveNext())
            apart2(mean.getNext());
        Mean.turnToMain(mean);
    }

    // 修改后
    public void modified() {
        saved = false;
        main.saveSet();
    }

    public void modifySaved() {
        saved = true;
        main.saveSet();
    }

    public boolean saved() {
        return saved;
    }

    public boolean opened() {
        return opened;
    }

    // 清空所有意象相关
    public void clear(boolean ask) {
        if (ask)
            if (JOptionPane.showConfirmDialog(main, "确认清空？", "清空所有意象", JOptionPane.YES_NO_OPTION) != 0)
                return;
        reserves.clear();
        hidden.clear();
        mainMean.clear();
        allMean.clear();
        view.clear();
        view.info_refreshInfo(view.ALL);
        main.slideDownMeun(false);
        main.jtfPrePgPgSet(0);// 设置为首页
    }

    public void revoke_last() {
        if (!reserves.isEmpty()) {
            modifyPointedText(reserves.get(reserves.size() - 1), Recover);
            reserves.remove(reserves.get(reserves.size() - 1));
        }
    }

    /**
     * 转为编辑模式
     */
    public void turnEditModel(boolean editModel) {
        main.rightPanel.setVisible(!editModel);
        textPane.setCaretPosition(0);
        if (editModel) {
            view.reSetOrder();
            view.callBackAll();
            recoveryTextColor();
        }
        this.editModel = editModel;
    }

    public void resetAllDescription() {
        for (Mean anAllMean : allMean) {
            anAllMean.setDescription();
        }
    }

    public String getContext() {
        return context;
    }

    public void setSource(String source) {
        context = null;
        this.source = source;
    }

    /**
     * 重置context，在修改文章后
     */
    public void resetContext() {
        if (main.presentPage == 0)
            context = outil.getTextExcepted13(textPane.getText());
        else {
            String a = main.presentPage == 1 ? "" : context.substring(0, getPgs().get(main.presentPage - 1)),
                    c = context.substring(getPgs().get(main.presentPage), context.length());
            context = outil.getTextExcepted13(a + textPane.getText() + c);
        }
    }

    public void setContext(String context) {
        source = null;
        this.context = context;
    }

    public TreeMap<Integer, Integer> getPgs() {
        return pages;
    }

    public Main getM() {
        return main;
    }

    public ViewControl getVc() {
        return view;
    }

    public JTextPane getText() {
        return textPane;
    }

}